(**

   Le   module  Protocole  permet   de  représenter   les  différentes
   informations que  nous avons  jugées utiles et  indispensables pour
   représenter une  partie de  tarot multijoueurs. Parmi  les éléments
   contre-intuitifs, on ne peut  pas refuser une invitation (il suffit
   de se  déconnecter et  de se reconnecter),  on ne travaille  pas en
   mode requête/réponse.

   Nous  avons choisi  les éléments  du protocole  pour un  client qui
   disposerait d'une  fonction de test d'égalité entre  des entiers et
   d'une fonction d'incrémentation d'entiers  modulo 5, mais pas d'une
   fonction de  comparaison d'enchères ou  de cartes. C'est  pour cela
   que nous ne disons pas quand  le client doit jouer (il le devine en
   fonction du  premier joueur, de son  numéro et du  nombre de cartes
   jouées),  mais  que  nous  disons   qui  remporte  le  pli  ou  les
   enchères. Nous  supposons aussi que  le client ne sait  pas quelles
   cartes  il a  le  droit  d'appeler, d'écarter  ou  de jouer,  c'est
   pourquoi nous fournissons {!msg.Refuse}.

   La partie se déroule en 3 temps :
   {ol
   {li Identification du nouveau client}
   {li Attente d'une partie}
   {li Jeu de la partie}}

   Dans l'étape 1, on suppose  que vous avez établi une connexion avec
   le serveur. Vous devez envoyer {!msg.Identifier}. Soit vous recevez
   un message {!msg.Entree}, auquel cas vous êtes identifié, soit vous
   recevez {!msg.Refuse},  auquel cas vous avez sollicité  un nom déjà
   pris. Vous pouvez recommencer.

   L'étape   2   se   termine   lorsque  vous   recevez   le   message
   {!msg.Noms}. Vous commencez par recevoir les messages {!msg.Entree}
   correspondant   aux  joueurs  déjà   présents  lorsque   vous  vous
   connectez,  puis  {!msg.Entree} avec  votre  propre  nom, puis  les
   messages {!msg.Entree} et  {!msg.Sortie} correspondant aux nouveaux
   joueurs disponibles  et aux joueurs  qui trouvent une partie  ou se
   déconnectent.   Vous pouvez  prendre  l'initiative d'{!msg.Inviter}
   les  joueurs que  vous voulez  ou attendre  une invitation.  Si les
   {!msg.Noms} de  vos partenaires ne  vous plaisent pas,  vous pouvez
   vous déconnecter pour annuler la partie.

   L'étape  3 se  termine lorsque  l'un des  joueurs de  la  partie se
   déconnecte.   Les 4  autres joueurs  sont alors  déconnectés.  Vous
   recevez   d'abord    les   messages   {!msg.Noms},   {!msg.Numero},
   {!msg.Distribution}, puis  le premier joueur  (celui qui a  reçu le
   numéro 0) prend l'initiative de faire une enchère via {!msg.Prise},
   ce    qui    provoque   l'envoi    à    tous    les   joueurs    de
   {!msg.Contrat}. Lorsque vous avez reçu autant de {!msg.Contrat} que
   votre numéro, c'est à vous. Une  fois que 5 contrats sont faits, le
   joueur qui  a fait  la plus grande  enchère reçoit  {!msg.Appel} et
   sait donc que c'est lui  le preneur. Il envoie donc {!msg.Appeler},
   ce qui  provoque l'envoi  à tous de  {!msg.ContratFinal}.  Ensuite,
   selon que  le contrat est  supérieur ou non, il  envoie directement
   {!msg.Jeu} (la partie commence)  ou {!msg.Chien}, ce qui indique au
   preneur qu'il doit faire  un écart, donc envoyer {!msg.Ecart}. Dans
   ce dernier cas, si l'écart  est légitime et contient des atouts, le
   serveur envoie à tous {!msg.Atout} puis {!msg.Jeu}.

   Ensuite, on joue des cartes en envoyant {!msg.Requete} dans l'ordre
   du tour : vous devez jouer  quand premier joueur + nombre de cartes
   jouées =  votre numéro.  Une fois  les 5 cartes  jouées, le serveur
   envoie {!msg.Pli} pour indiquer le nouveau premier joueur.  On fait
   ainsi 15 tours.

   Au bout  de 15 tours, le  serveur envoie {!msg.Resultat}  à tout le
   monde, puis {!msg.Noms} (il y a eu une permutation cyclique : celui
   qui  jouait   en  deuxième   joue  maintenant  en   premier),  puis
   {!msg.Numero}  puis {!msg.Distribution},  et la  partie recommence.
   Les {!msg.Resultat}s sont indépendants d'une partie à l'autre : ils
   ne sont pas sommés.

   @author Vivien Kraus

*)

(**

   Le type représentant le message. Vous avez la liberté de la manière
   dont vous l'envoyez au serveur  : service JSON avec un serveur web,
   passage par une socket TCP en mode connecté, ...

*)
type msg =
  | Deconnexion 
  (** 
      
      Ceci n'est  pas un message qu'on  doit envoyer. C'est  à vous de
      l'intercepter et  de le traduire.  Plus précisément,  si on vous
      demande  d'envoyer  {!msg.Deconnexion},  vous  devez  fermer  la
      communication, et si vous  voulez faire comprendre que vous avez
      perdu  la  connexion,  vous  devez faire  lire  (cela  s'appelle
      assimiler    dans    le    module    {!Tarotv})    le    message
      {!msg.Deconnexion}.

  *)
      
  | ErreurProtocole 
  (** 

      Serveur  ->   client  :  vous   n'avez  pas  fait   une  requête
      valide. Évitez de jouer au perroquet côté client...

  *)
      
  | Refuse (** Serveur -> client : 
	       votre requête n'est pas acceptable.*)

  | Identifier of string 
  (**

     client -> serveur : je veux  prendre ce nom. Ma requête peut être
     refusée si quelqu'un porte déjà ce nom ou s'il est réservé.

  *)

  | Inviter of string array
  (** 
      
      client -> serveur  : je veux jouer avec ces  4 types. Ma requête
      peut  être  refusée si  j'invite  plus  de  4 personnes,  si  je
      m'invite  moi-même,  si  j'inite   des  gens  qui  ne  sont  pas
      disponibles. On ne demande pas l'avis des gens concernés.
      
  *)

  | Entree of string 
  (** 
      
      Si  je  ne  suis pas  en  jeu,  cela  veut  dire que  le  client
      correspondant  peut être  invité.   La réception  de ce  message
      signifie que j'ai réussi à m'identifier.
  
  *)
      
  | Sortie of string
  (**
     
     Si je  ne suis pas  en jeu,  cela veut dire  que je ne  peux plus
     inviter ce joueur. Soit il s'est déconnecté, soit il a trouvé une
     partie.
     
  *)
      
  | Noms of string array
  (**
     
     Je joue maintenant dans la  partie comprenant ces 5 joueurs, dont
     moi. Si je n'étais pas en  jeu juste avant, tous les joueurs hors
     jeu  ont  reçu le  message  {!msg.Sortie}.  Si  j'étais en  train
     d'inviter  des personnes,  cela  signifie que  ma  requête a  été
     acceptée (ou  qu'on m'a invité  entre-temps, dans ce cas  il faut
     vérifier que les 5 noms  correspondent à moi et aux personnes que
     j'ai invitées).
     
  *)
      
  | Numero of int
  (**
     
     J'ai  reçu ce  numéro.   Je  garderai le  même  pendant toute  la
     partie. Il  correspond à  la place de  mon nom dans  le précédent
     message {!msg.Noms},  et sera celui qui sera  émis par {!msg.Pli}
     lorsque  j'aurai  remporté  le  pli  à  un  certain  tour.  C'est
     également   l'indice   de   mon   score  dans   le   tableau   de
     {!msg.Resultat}.

     Ce numéro peut paraître inutile vu qu'on a envoyé {!msg.Noms}, où
     je  n'ai qu'à  retrouver  mon  nom pour  trouver  mon numéro.  Ce
     message  {!msg.Numero}  était  employé   dans  la  version  1  du
     protocole, où on n'avait pas le droit de choisir son nom.
     
  *)

  | Distribution of Carte.carte array
  (**

     Je reçois  mes 15  cartes, dans notre  ordre canonique  (mais pas
     celui    de   la    FFT),   c'est-à-dire    dans    l'ordre   des
     {!Carte.int_of_carte}.  Si  j'ai le numéro  0, je dois  faire une
     enchère maintenant.

  *)

  | Prise of int
  (**

     Je souhaite faire cette enchère : 
     {ul
     {- 0 : passer,}
     {- 1 : faire une petite,}
     {- 2 : garder,}
     {- 3 : garder sans le chien,}
     {- 4 : garder contre le chien.}}

  *)

  | Contrat of int
  (**
     
     Le joueur dont c'était le tour a fait cette enchère.

  *)

  | Appel
  (** 
      
      Vous  qui   avez  reçu  ce  message,  vous   devez  appeler  une
      carte. C'est vous le preneur.

  *)

  | Appeler of Carte.carte
  (**
     
     Moi  qui ai  reçu le  message {!msg.Appel},  je  décide d'appeler
     cette carte.

  *)

  | ContratFinal of int * int * Carte.carte (*preneur - prise - appel *)
  (**
     
     À tous : si je reçois  [msg.ContratFinal (j, e, c)], ça veut dire
     que le preneur  est le joueur numéro [j],  qu'il a fait l'enchère
     [e] en appelant la carte [c].

  *)

  | Chien of Carte.carte array
  (**

     À tous  : le contrat n'étant  pas un contrat  supérieur, le chien
     est dévoilé.  Celui qui  avait reçu {!msg.Appel}  doit maintenant
     faire un {!msg.Ecart}.

  *)

  | Ecart of Carte.carte array
  (** 

      Moi, le preneur, je fais cet écart.

  *)

  | Atout of Carte.carte list
  (**
     
     À tous : le preneur a mis ces atouts dans son écart.

  *)

  | Jeu
  (**
     
     À tous : la partie commence. Joueur 0, c'est à toi.

  *)

  | Requete of Carte.carte
  (**
     
    Moi, le client dont c'est le tour, veut jouer cette carte. 
     
  *)

  | Carte of Carte.carte
  (**
     
     À tous : le joueur dont c'était le tour a joué cette carte.
     
  *)

  | Pli of int (* le joueur *)
  (**

     À tous : c'est  ce jouer qui a remporté le pli,  c'est donc à lui
     de jouer.

  *)

  | Resultat of int array
(**
   
   À tous : les résultats pour  cette manche sont les suivants : si on
   reçoit [Protocole.Resultat  tableau], et que mon  numéro est [num],
   alors j'ai fait le score [tableau.(num)].

*)

(**

   Vous  êtes libre  de communiquer  ces éléments  de protocole  de la
   manière qui vous plaît. Cependant, nous avons fait la même chose en
   c++, et  pour que  les entités puissent  communiquer à  travers les
   langages,  nous avons  défini  une forme  pour  ces messages.   Ils
   doivent  passer par  des connexions  TCP en  mode connecté  sous la
   forme suivante :

   {ul   
   {- sur un octet non signé : la taille totale (donc en tenant compte
   de cet octet) du paquet représentant le message,}   
   {-  sur   un  octet  non  signé   :  le  numéro  de   type  ;  voir
   l'implémentation de  {!ecrire_message} pour la  correspondance avec
   le type {!msg},}   
   {- le  corps du message. Si c'est  un nom, c'est sur  16 octets (en
   remplissant  avec des  octets nuls),  préférablement en  UTF-8.  Si
   c'est une carte, c'est  le numéro retourné par {Carte.int_of_carte}
   sur un octet  non signé.  Si c'est un joueur  ou une enchère, c'est
   un numéro entre 0 et 4 sur un octet non signé. Si c'est un tableau,
   les éléments sont les uns après les autres.  Leur nombre est défini
   :  chien,  écart  ->  3  éléments,  distribution  ->  15  éléments,
   résultat, noms ->  5 éléments. Chaque élément de  résultat est codé
   sur en entier de 2 octets,  signé. Enfin, pour la liste des atouts,
   on lit des  cartes jusqu'à ce que tout le paquet  soit lu (rappel :
   on a envoyé sa taille).}}

   Attention, piège :  le message {!msg.Jeu} doit être  envoyé avec un
   numéro 0 sur 1 octet.

*)

(**

   [ecrire_message message] retourne les octets à envoyer dans l'ordre
   pour transmettre le message selon la norme ci-dessus. Ne marche pas
   sur {!msg.Deconnexion}.

*)
val ecrire_message: msg -> int array 
(* Les octets à envoyer. Ne garder que les 8 derniers bits. *)

(**

   [lire_message  ()] renvoie  une fonction  [lecteur], qui  servira à
   déchiffrer les messages reçus lorsqu'on envoie les octets un par un
   selon la norme ci-dessus.

   Une fois en possession  de [lecteur], appelez-le successivement sur
   les octets  reçus. La valeur  retournée ne sera pas  None lorsqu'un
   message aura été compris.

*)
val lire_message: unit -> (int -> msg option)
(* lire_message () renvoie une fonction, à qui on indique chaque octet lu. 
 Lorsqu'un message est complet, elle renvoie le message. *)
